/////////////////////////////////////////////////////////////////////////////
// Name:        varianthandlers.cpp
// Purpose:     Handlers to create, read and write variants
// Author:      Julian Smart
// Modified by:
// Created:     2006-05-11
// RCS-ID:      $$
// Copyright:   (c) Julian Smart
// Licence:     New BSD License
/////////////////////////////////////////////////////////////////////////////

#include "wx/wx.h"

#include "wx/module.h"
#include "wx/datstrm.h"

#include "variantutils/varianthandlers.h"

IMPLEMENT_CLASS(wxVariantHandler, wxObject)

/*
 * wxStandardVariantHandler knows how to create, load and save the basic variant types.
 */

class wxStandardVariantHandler: public wxVariantHandler
{
    DECLARE_DYNAMIC_CLASS(wxStandardVariantHandler)
public:
    wxStandardVariantHandler(const wxString& name = wxEmptyString)
        : wxVariantHandler(name)
        { }

    virtual bool ReadValue(const wxString& type, wxVariant& variant, wxInputStream& stream, wxDataInputStream& dataStream);
    virtual bool WriteValue(const wxVariant& variant, wxOutputStream& stream, wxDataOutputStream& dataStream);

    virtual bool CanHandle(const wxString& type);

    /// Supply a default value for this type
    virtual wxVariant GetDefaultValue(const wxString& type);
};


/*
 * wxVariantHandlers stores objects that know about particular variant types
 */

wxList wxVariantHandlers::sm_handlers;

bool wxVariantHandlers::CanHandle(const wxString& type)
{
    return (NULL != FindHandlerByType(type));
}

bool wxVariantHandlers::Read(wxVariant& variant, wxInputStream& stream, wxDataInputStream& dataStream)
{
    wxString name, type;
    type = dataStream.ReadString();
    name = dataStream.ReadString();
    variant.SetName(name);

    wxVariantHandler* handler = FindHandlerByType(type);
    if (handler)
    {
        return handler->ReadValue(type, variant, stream, dataStream);
    }
    return false;
}

bool wxVariantHandlers::Write(const wxVariant& variant, wxOutputStream& stream, wxDataOutputStream& dataStream)
{
    dataStream.WriteString(variant.GetType());
    dataStream.WriteString(variant.GetName());

    wxVariantHandler* handler = FindHandlerByType(variant.GetType());
    if (handler)
    {
        return handler->WriteValue(variant, stream, dataStream);
    }
    return false;
}

void wxVariantHandlers::AddHandler( wxVariantHandler *handler )
{
    sm_handlers.Append( handler );
}

void wxVariantHandlers::InsertHandler( wxVariantHandler *handler )
{
    sm_handlers.Insert( handler );
}

bool wxVariantHandlers::RemoveHandler( const wxString& name )
{
    wxVariantHandler *handler = FindHandlerByName(name);
    if (handler)
    {
        sm_handlers.DeleteObject(handler);
        delete handler;
        return true;
    }
    else
        return false;
}

wxVariantHandler *wxVariantHandlers::FindHandlerByName( const wxString& name )
{
    wxList::compatibility_iterator node = sm_handlers.GetFirst();
    while (node)
    {
        wxVariantHandler *handler = (wxVariantHandler*)node->GetData();
        if (handler->GetName().Cmp(name) == 0) return handler;

        node = node->GetNext();
    }
    return 0;
}

wxVariantHandler *wxVariantHandlers::FindHandlerByType(const wxString& type)
{
    wxList::compatibility_iterator node = sm_handlers.GetFirst();
    while (node)
    {
        wxVariantHandler *handler = (wxVariantHandler*)node->GetData();
        if (handler->CanHandle(type))
            return handler;
        node = node->GetNext();
    }
    return NULL;
}

void wxVariantHandlers::InitStandardHandlers()
{
    AddHandler(new wxStandardVariantHandler(wxT("Standard wxVariant handler")));
}

void wxVariantHandlers::CleanUpHandlers()
{
    wxList::compatibility_iterator node = sm_handlers.GetFirst();
    while (node)
    {
        wxVariantHandler *handler = (wxVariantHandler *)node->GetData();
        wxList::compatibility_iterator next = node->GetNext();
        delete handler;
        node = next;
    }

    sm_handlers.Clear();
}

/// Gets a default value for this type
wxVariant wxVariantHandlers::GetDefaultValue(const wxString& type)
{
    wxVariantHandler* handler = FindHandlerByType(type);
    if (handler)
        return handler->GetDefaultValue(type);
    else
        return wxVariant();
}


// A module to allow wxVariantHandlers initialization/cleanup

class wxVariantHandlersModule: public wxModule
{
DECLARE_DYNAMIC_CLASS(wxVariantHandlersModule)
public:
    wxVariantHandlersModule() {}
    bool OnInit() { wxVariantHandlers::InitStandardHandlers(); return true; };
    void OnExit() { wxVariantHandlers::CleanUpHandlers(); };
};

IMPLEMENT_DYNAMIC_CLASS(wxVariantHandlersModule, wxModule)

/*
 * wxStandardVariantHandler knows how to create, load and save the basic variant types.
 */

IMPLEMENT_DYNAMIC_CLASS(wxStandardVariantHandler, wxVariantHandler)

bool wxStandardVariantHandler::ReadValue(const wxString& type, wxVariant& variant, wxInputStream& stream, wxDataInputStream& dataStream)
{
    if (type == wxT("string"))
    {
        wxString value = dataStream.ReadString();
        variant = value;
        return true;
    }
    else if (type == wxT("null"))
    {
        return true;
    }
    else if (type == wxT("list"))
    {
        variant.NullList();
        int n = dataStream.Read16();
        int i;
        for (i = 0; i < n; i++)
        {
            wxVariant var;
            wxVariantHandlers::Read(var, stream, dataStream);

            variant.Append(var);
        }
        return true;
    }
    else if (type == wxT("stringlist"))
    {
        wxStringList stringList;
        int n = dataStream.Read16();
        int i;
        for (i = 0; i < n; i++)
        {
            wxString val = dataStream.ReadString();

            stringList.Add(val);
        }
        variant = stringList;
        return true;
    }
    else if (type == wxT("arrstring"))
    {
        wxArrayString stringArr;
        int n = dataStream.Read16();
        int i;
        for (i = 0; i < n; i++)
        {
            wxString val = dataStream.ReadString();

            stringArr.Add(val);
        }
        variant = stringArr;
        return true;
    }
    else if (type == wxT("long"))
    {
        wxUint64 value;
        value = dataStream.Read64();
        variant = (long) (wxInt64) value;
        return true;
    }
    else if (type == wxT("double"))
    {
        double value = dataStream.ReadDouble();
        variant = value;
        return true;
    }
    else if (type == wxT("bool"))
    {
        wxUint8 value;
        value = dataStream.Read8();
        variant = (bool) (value != 0);
        return true;
    }
    else if (type == wxT("char"))
    {
        char value = (char) dataStream.Read8();
        variant = (wxChar) value;
        return true;
    }
    else if (type == wxT("void*"))
    {
        return true;
    }
    else if (type == wxT("datetime"))
    {
        wxDateTime dateTime;
        wxString value;
        dataStream >> value;
        if (!value.IsEmpty())
            dateTime.ParseDateTime(value);

        variant = dateTime;
        return true;
    }
    else
    {
        wxASSERT_MSG( false, wxT("Unknown variant type"));
        return false;
    }

    return true;
}

bool wxStandardVariantHandler::WriteValue(const wxVariant& variant, wxOutputStream& stream, wxDataOutputStream& dataStream)
{
    wxString type(variant.GetType());
    wxASSERT( !type.IsEmpty() );

    if (type == wxT("string"))
    {
        dataStream << variant.GetString();
        return true;
    }
    else if (type == wxT("null"))
    {
        return true;
    }
    else if (type == wxT("list"))
    {
        size_t i;
        dataStream.Write16(variant.GetCount());

        for (i = 0; i < variant.GetCount(); i++)
        {
            wxVariantHandlers::Write(variant[i], stream, dataStream);
        }
        return true;
    }
#if !wxCHECK_VERSION(2,7,2) || WXWIN_COMPATIBILITY_2_4
    else if (type == wxT("stringlist"))
    {
        wxStringList& stringList = variant.GetStringList();
        dataStream.Write16(stringList.GetCount());
        size_t i;
        for (i = 0; i < stringList.GetCount(); i++)
        {
            dataStream << stringList[i];
        }
        return true;
    }
#endif
    else if (type == wxT("arrstring"))
    {
        wxArrayString stringArr = variant.GetArrayString();
        dataStream.Write16(stringArr.GetCount());
        size_t i;
        for (i = 0; i < stringArr.GetCount(); i++)
        {
            dataStream << stringArr[i];
        }
        return true;
    }
    else if (type == wxT("long"))
    {
        dataStream.Write64((wxUint64) variant.GetLong());
        return true;
    }
    else if (type == wxT("double"))
    {
        dataStream.WriteDouble(variant.GetDouble());
        return true;
    }
    else if (type == wxT("bool"))
    {
        dataStream.Write8((wxUint8) variant.GetBool());
        return true;
    }
    else if (type == wxT("char"))
    {
        dataStream.Write8((wxUint8) variant.GetChar());
        return true;
    }
    else if (type == wxT("void*"))
    {
        return true;
    }
    else if (type == wxT("datetime"))
    {
        wxDateTime dateTime = variant.GetDateTime();
        if (dateTime.IsValid())
            dataStream << dateTime.Format();
        else
            dataStream << wxEmptyString;
        return true;
    }
    else
    {
        wxASSERT_MSG( false, wxT("Unknown variant type"));
        return false;
    }

    return true;
}

bool wxStandardVariantHandler::CanHandle(const wxString& type)
{
    if (type == wxT("string"))
    {
        return true;
    }
    else if (type == wxT("null"))
    {
        return true;
    }
    else if (type == wxT("list"))
    {
        return true;
    }
    else if (type == wxT("stringlist"))
    {
        return true;
    }
    else if (type == wxT("arrstring"))
    {
        return true;
    }
    else if (type == wxT("long"))
    {
        return true;
    }
    else if (type == wxT("double"))
    {
        return true;
    }
    else if (type == wxT("bool"))
    {
        return true;
    }
    else if (type == wxT("char"))
    {
        return true;
    }
    else if (type == wxT("void*"))
    {
        return true;
    }
    else if (type == wxT("datetime"))
    {
        return true;
    }
    else
    {
        return false;
    }
}

/// Supply a default value for this type
wxVariant wxStandardVariantHandler::GetDefaultValue(const wxString& type)
{
    if (type == wxT("string"))
    {
        return wxVariant(wxEmptyString);
    }
    else if (type == wxT("null"))
    {
        return wxVariant();
    }
    else if (type == wxT("list"))
    {
        wxList list;
        return wxVariant(list);
    }
    else if (type == wxT("stringlist"))
    {
        wxStringList list;
        return wxVariant(list);
    }
    else if (type == wxT("arrstring"))
    {
        wxArrayString arr;
        return wxVariant(arr);
    }
    else if (type == wxT("long"))
    {
        return wxVariant((long) 0);
    }
    else if (type == wxT("double"))
    {
        return wxVariant((double) 0.0);
    }
    else if (type == wxT("bool"))
    {
        return wxVariant(false);
    }
    else if (type == wxT("char"))
    {
        return wxVariant(wxT(' '));
    }
    else if (type == wxT("void*"))
    {
        return wxVariant((void*) NULL);
    }
    else if (type == wxT("datetime"))
    {
        return wxVariant(wxDateTime());
    }
    else
    {
        return wxVariant();
    }
}

/// Read a variant
bool wxReadVariant(wxVariant& variant, wxInputStream& stream, wxDataInputStream& dataStream)
{
    return wxVariantHandlers::Read(variant, stream, dataStream);
}

/// Write a variant
bool wxWriteVariant(const wxVariant& variant, wxOutputStream& stream, wxDataOutputStream& dataStream)
{
    return wxVariantHandlers::Write(variant, stream, dataStream);
}

/// Insertion and extraction operators
wxOutputStream& operator<<(wxOutputStream& stream, const wxVariant& variant)
{
    wxDataOutputStream dataStream(stream);
    wxVariantHandlers::Write(variant, stream, dataStream);
    return stream;
}

wxInputStream& operator>>(wxInputStream& stream, wxVariant& variant)
{
    wxDataInputStream dataStream(stream);
    wxVariantHandlers::Read(variant, stream, dataStream);
    return stream;
}
